package org.reenscape.model;
import org.reenscape.updater.NpcUpdater;
import org.reenscape.updater.ClientUpdater;
import org.reenscape.lib.Library;
import org.reenscape.lib.Item;
import org.reenscape.lib.stream;
import org.reenscape.*;
import java.io.*;
import java.util.Calendar;
import java.util.GregorianCalendar;

public abstract class Player {


			public void println_debug(String str) {
				System.out.println("[player-"+playerId+"]: "+str);
			}
			
			public void println(String str) {
				System.out.println("[player-"+playerId+"]: "+str);
			}

			public int Delay = 0;
			public boolean newhptype = false;
			public int hptype = 0;
			public boolean poisondmg = false;
			public int FocusPointX = -1, FocusPointY = -1;
			public boolean SeenUpdate = false;
			public int design = 0;
			public int CombatMask = 126;
  
			public void RotateToPoint(int pointX, int pointY) {
				FocusPointX = 2*pointX+1;
				FocusPointY = 2*pointY+1;
			}

			public int GetPlrShieldBlockAnim(int id) {
				return 0x484;
			}
			
			public void playerGfx(int id, int delay) {
				mask100var1 = id;
				mask100var2 = delay;
				mask100update = true;
				updateRequired = true;
			}
			
			public boolean checkBarricade(int X, int Y){
				for (int i =0; i <Server.npcUpdater.maxNPCs; i++)
					if (Server.npcUpdater.npcs[i] != null)
						if (Server.npcUpdater.npcs[i].npcType == 1532 ||Server.npcUpdater.npcs[i].npcType == 1533 ||Server.npcUpdater.npcs[i].npcType == 1534 ||Server.npcUpdater.npcs[i].npcType == 1535)
							if (Server.npcUpdater.npcs[i].absX == X && Server.npcUpdater.npcs[i].absY == Y)
								return false;
				return true;
			}
			
			public boolean ShowCharacterDesign = false;
			public int configPrivateChat = 0; // 0 on, 1 friends, 2 off
			
			public void BruteChatText(String ia) {
				String temp = ia;
				byte[] ct = new byte[4096];
				Library.mutil.textPack(ct, temp);
				Integer i = 1;
					for (i = 1; i < 1000; i++) {
						String t = Library.mutil.textUnpack(ct, i);
						if (t.equalsIgnoreCase(temp)) {
							break;
						}
					}
				chatTextUpdateRequired = true;
				chatText = ct;
				chatTextSize = i.byteValue();
				}
				
			public boolean ChangeDoor[] = new boolean[Server.objectUpdater.MaxObjects];
			
			public void BrutePM(String ia, String sender, int prights) {
				String temp = ia;
				byte[] ct = new byte[100];
				Library.mutil.textPack(ct, temp);
				int i = 1;
					for (i = 1; i < 100; i++) {
						String t = Library.mutil.textUnpack(ct, i);
						if (t.equalsIgnoreCase(temp)) {
						break;
						}
					}
				PacketParser p = (PacketParser) Server.clientUpdater.players[playerId];
				p.sendpm(Library.mutil.playerNameToInt64(sender), prights, ct, i);
			}
  
			private void appendSetFocusDestination(stream str) {
				str.writeWordBigEndianA(FocusPointX);
				str.writeWordBigEndian(FocusPointY);
			}
  public int correctPin = 0;
  public int bankSet = 0;
  // some remarks: one map region is 8x8
  // a 7-bit (i.e. 128) value thus ranges over 16 such regions
  // the active area of 104x104 is comprised of 13x13 such regions, i.e. from
  // the center region that is 6 regions in each direction (notice the magical 6
  // appearing also in map region arithmetics...)
  public int combat = 0;
  public Player(int _playerId) {
    playerId = _playerId;
    playerRights = 0;
    
    for (int i = 0; i < playerItems.length; i++) { //Setting player items
      playerItems[i] = 0;
    }
    for (int i = 0; i<playerItemsN.length; i++) { //Setting Item amounts
      playerItemsN[i] = 0;
    }
	
		for (int i = 0; i<playerEquipmentN.length; i++) { //Setting Worn Item amounts
			playerEquipmentN[i] = 0;
		}
		for (int i = 0; i < playerEquipment.length; i++) { //Setting Worn player items
			playerEquipment[i] = -1;
		}
    
    for (int i=0; i<playerLevel.length; i++) { //Setting Levels
      if (i == 3) {
        playerLevel[i] = 10;
        playerXP[i] = 1155;
      } else {
        playerLevel[i] = 1;
        playerXP[i] = 0;
      }
    }
    
    for (int i = 0; i< playerBankSize; i++) { //Setting bank items
      bankItems[i] = 0;
    }
    
    for (int i = 0; i < playerBankSize; i++) { //Setting bank item amounts
      bankItemsN[i] = 0;
    }
    
    for (int i = 0; i < playerEquipment.length; i++) {
      playerEquipment[i] = -1;
      playerEquipmentN[i] = 0;
    }
    //Perhaps by quoting the above out it will stop resetting users before loading, so the user can't be reset,
    //also making the playerName equal the player id it stops it from saving unless a savedgame was loaded...
    //tried, first thing i heard was "lmao i got reset for the 3rd time!"...ffs!
    
    //Setting Welcomescreen information
    Calendar cal = new GregorianCalendar();
    int day = cal.get(Calendar.DAY_OF_MONTH);
    int month = cal.get(Calendar.MONTH);
    int year = cal.get(Calendar.YEAR);
    int calc = ((year * 10000) + (month * 100) + day);
    playerLastLogin = calc;
    playerLastConnect = "";
    playerIsMember = 1;
    playerMessages = 0;
    
    //Setting player standard look
    playerLook[0] = 0;
    playerLook[1] = 0;
    playerLook[2] = 0;
    playerLook[3] = 0;
    playerLook[4] = 0;
    playerLook[5] = 0;
    
    pHead=7;
    pTorso=25;
    pArms=29;
    pHands=35;
    pLegs=39;
    pFeet=44;
    pBeard=14;
    
    // initial x and y coordinates of the player
    heightLevel = 0;
    teleportToX = 3221;
    teleportToY = 3219;
    
    // client initially doesn't know those values yet
    absX = absY = -1;
    mapRegionX = mapRegionY = -1;
    currentX = currentY = 0;
    resetWalkingQueue();
  }
  
  public void destruct() {
    playerListSize = 0;
    for(int i = 0; i < maxPlayerListSize; i++) playerList[i] = null;
    npcListSize = 0;
    for(int i = 0; i < maxNPCListSize; i++) npcList[i] = null;
    
    absX = absY = -1;
    mapRegionX = mapRegionY = -1;
    currentX = currentY = 0;
    resetWalkingQueue();
  }
  public boolean isNpc;
  public int npcId;
  public boolean initialized = false, disconnected = false, savefile = true;
  public boolean isActive = false;
  public boolean isKicked = false;
  public boolean CrackerMsg = false;
  public boolean CrackerForMe = false;
  public boolean IsGhost = false;
  
  public int actionTimer = 0;
  public int actionAmount = 0;
  public String actionName = "";
  
  public String connectedFrom = "";
  public String globalMessage = "";
  
  public int killcount = 0;
  public int deathcount = 0;
  public int pkpoints = 0;
  
  public int AttackingOn = 0;
  
  public int OptionObject = -1;
  
  public boolean Climbing = false;
  public int ClimbStage = -1;
  
  public int hiddenPoints; // number of places found ;)
  public int foundz[] = new int[100]; // used for secret places being found ;)
  
  public int[] clueItems = new int[28];
  public int[] clueItemsN = new int[28];
  
  public int ActionType = -1;
  public int destinationX = -1;
  public int destinationY = -1;
  public int destinationID = -1;
  public int destinationRange = 1;
  public boolean WalkingTo = false;
  
  public int playerAxe = -1;
  public boolean IsWcing = false;
  public int tradeRequest = 0;
  public int tradeDecline = 0;
  public int tradeWith = 0;
  public int tradeWaitingTime = 0;
  public int tradeStatus = 0;
  public boolean tradeUpdateOther = false;
  public boolean tradeOtherDeclined = false;
  public int[] playerTItems = new int[28]; //player Trade Items
  public int[] playerTItemsN = new int[28];
  public int[] playerOTItems = new int[28]; //player Other Trade Items
  public int[] playerOTItemsN = new int[28];
  public boolean takeAsNote = false;
  
  public abstract void initialize();
  public abstract void update();
  
  public int playerId = -1;		// -1 denotes world is full, otherwise this is the playerId
  // corresponds to the index in Player players[]
  
  public String playerName = null;			// name of the connecting client
  public String playerPass = null;			// name of the connecting client
  public boolean isRunning2 = false;
  public boolean stoprunning = false;
  
  
  public int playerRights;		// 0=normal player, 1=player mod, 2=jmod, 3=root
  
  public ClientUpdater handler = null;
  
  public int maxItemAmount = 999999999; //999,999,999
  
  public int[] playerItems = new int[28];
  public int[] playerItemsN = new int[28];
  
  public int playerBankSize = 59;
  public int[] bankItems = new int[800];
  public int[] bankItemsN = new int[800];
  public boolean bankNotes = false;
  
  
  //Default appearance
  public int pHead;
  public int pTorso;
  public int pArms;
  public int pHands;
  public int pLegs;
  public int pFeet;
  public int pBeard;
  public int pEmote = 0x328; // this being the original standing state
  public int pWalk = 0x333; // original walking animation
  public boolean apset = false;
  
  public int headIcon = 0;
  
  public int[] playerEquipment = new int[14];
  public int[] playerEquipmentN = new int[14];
  
  public int playerHat = 0;
  public int playerCape = 1;
  public int playerAmulet = 2;
  public int playerWeapon = 3;
  public int playerChest = 4;
  public int playerShield = 5;
  public int playerLegs = 7;
  public int playerHands = 9;
  public int playerFeet = 10;
  public int playerRing = 12;
  public int playerArrows = 13;
  
  public int playerAttack = 0;
  public int playerDefence = 1;
  public int playerStrength = 2;
  public int playerHitpoints = 3;
  public int playerRanged = 4;
  public int playerPrayer = 5;
  public int playerMagic = 6;
  public int playerCooking = 7;
  public int playerWoodcutting = 8;
  public int playerFletching = 9;
  public int playerFishing = 10;
  public int playerFiremaking = 11;
  public int playerCrafting = 12;
  public int playerSmithing = 13;
  public int playerMining = 14;
  public int playerHerblore = 15;
  public int playerAgility = 16;
  public int playerThieving = 17;
  public int playerSlayer = 18;
  public int playerFarming = 19;
  public int playerRunecrafting = 20; // Dis be max level
  
  public int i = 0;
  
  public int[] playerLevel = new int[25]; //Limit problem?
  public double[] playerXP = new double[25]; //^_  0-20 dont need anymore.
  public int currentHealth = playerLevel[playerHitpoints];
  public int maxHealth = playerLevel[playerHitpoints];
  
  // the list of players currently seen by thisPlayer
  // this has to be remembered because the client will build up exactly the same list
  // and will be used on subsequent player movement update packets for efficiency
  public final static int maxPlayerListSize = ClientUpdater.maxPlayers;
  public Player playerList[] = new Player[maxPlayerListSize];
  public int playerListSize = 0;
  // bit at position playerId is set to 1 incase player is currently in playerList
  public byte playerInListBitmap[] = new byte[(ClientUpdater.maxPlayers+7) >> 3];
  
  // the list of npcs currently seen by thisPlayer
  // this has to be remembered because the client will build up exactly the same list
  // and will be used on subsequent player movement update packets for efficiency
  public final static int maxNPCListSize = NpcUpdater.maxNPCs;
  public NPC npcList[] = new NPC[maxNPCListSize];
  public int npcListSize = 0;
  // bit at position npcId is set to 1 incase player is currently in playerList
  public byte npcInListBitmap[] = new byte[(NpcUpdater.maxNPCs+7) >> 3];
  	public boolean[] IsFireShowed = new boolean[Server.objectUpdater.MaxObjects];
	public boolean[] FireDelete = new boolean[Server.objectUpdater.MaxObjects];
	public boolean IsFireing = false;
	public boolean ForceWalk = false;
  
  // supported within the packet adding new players are coordinates relative to thisPlayer
  // that are >= -16 and <= 15 (i.e. a signed 5-bit number)
  public boolean withinDistance(Player otherPlr) {
    if(heightLevel != otherPlr.heightLevel) return false;
    int deltaX = otherPlr.absX-absX, deltaY = otherPlr.absY-absY;
    return deltaX <= 15 && deltaX >= -16 && deltaY <= 15 && deltaY >= -16;
  }
  
  public boolean withinDistance(NPC npc) {
    if (heightLevel != npc.heightLevel) return false;
    if (npc.NeedRespawn == true) return false;
    int deltaX = npc.absX-absX, deltaY = npc.absY-absY;
    return deltaX <= 15 && deltaX >= -16 && deltaY <= 15 && deltaY >= -16;
  }
  
  
  public int mapRegionX, mapRegionY;		// the map region the player is currently in
  public int absX, absY;					// absolute x/y coordinates
  public int currentX, currentY;			// relative x/y coordinates (to map region)
  // Note that mapRegionX*8+currentX yields absX
  public int heightLevel;		// 0-3 supported by the client
  
  public boolean updateRequired = true;		// set to true if, in general, updating for this player is required
  // i.e. this should be set to true whenever any of the other
  // XXXUpdateRequired flags are set to true
  // Important: this does NOT include chatTextUpdateRequired!
  
  // walking related stuff - walking queue etc...
  public static final int walkingQueueSize = 50;
  public int walkingQueueX[] = new int[walkingQueueSize], walkingQueueY[] = new int[walkingQueueSize];
  public int wQueueReadPtr = 0;		// points to slot for reading from queue
  public int wQueueWritePtr = 0;		// points to (first free) slot for writing to the queue
  public boolean isRunning = false;
  public int teleportToX = -1, teleportToY = -1;	// contain absolute x/y coordinates of destination we want to teleport to
  
  
  
  public void resetWalkingQueue() {
    wQueueReadPtr = wQueueWritePtr = 0;
    // properly initialize this to make the "travel back" algorithm work
    for(int i = 0; i < walkingQueueSize; i++) {
      walkingQueueX[i] = currentX;
      walkingQueueY[i] = currentY;
    }
  }
  
  public void addToWalkingQueue(int x, int y) {
    int next = (wQueueWritePtr+1) % walkingQueueSize;
    if(next == wQueueWritePtr) return;		// walking queue full, silently discard the data
    walkingQueueX[wQueueWritePtr] = x;
    walkingQueueY[wQueueWritePtr] = y;
    wQueueWritePtr = next;
  }
  
  // returns 0-7 for next walking direction or -1, if we're not moving
  public int getNextWalkingDirection() {
    if(wQueueReadPtr == wQueueWritePtr) return -1;		// walking queue empty
    int dir;
    do {
      dir = Library.mutil.direction(currentX, currentY, walkingQueueX[wQueueReadPtr], walkingQueueY[wQueueReadPtr]);
      if(dir == -1) wQueueReadPtr = (wQueueReadPtr+1) % walkingQueueSize;
      else if((dir&1) != 0) {
        System.out.println("["+playerName+"-"+playerId+"] Invalid waypoint in walking queue: "+dir+".");
        resetWalkingQueue();
        return -1;
      }
    } while(dir == -1 && wQueueReadPtr != wQueueWritePtr);
    if(dir == -1) return -1;
    dir >>= 1;
    /*MOD: NO WALKING THROUGH BARRICADES*/
    int tempabsX = absX+Library.mutil.directionDeltaX[dir];
    int tempabsY = absY+Library.mutil.directionDeltaY[dir];
    if (checkBarricade(tempabsX,tempabsY)){
      /*END MOD*/
      currentX += Library.mutil.directionDeltaX[dir];
      currentY += Library.mutil.directionDeltaY[dir];
      absX += Library.mutil.directionDeltaX[dir];
      absY += Library.mutil.directionDeltaY[dir];
      return dir;
      /*MOD BARRICADES*/
    }
    return -1;
    /*END MOD*/
  }
  
  // calculates directions of player movement, or the new coordinates when teleporting
  public boolean didTeleport = false;		// set to true if char did teleport in this cycle
  public boolean mapRegionDidChange = false;
  public int dir1 = -1, dir2 = -1;		// direction char is going in this cycle
  public int poimiX = 0, poimiY = 0;
  public void getNextPlayerMovement() {
    mapRegionDidChange = false;
    didTeleport = false;
    dir1 = dir2 = -1;
    
    if(teleportToX != -1 && teleportToY != -1) {
      mapRegionDidChange = true;
      if(mapRegionX != -1 && mapRegionY != -1) {
        // check, whether destination is within current map region
        int relX = teleportToX-mapRegionX*8, relY = teleportToY-mapRegionY*8;
        if(relX >= 2*8 && relX < 11*8 && relY >= 2*8 && relY < 11*8)
          mapRegionDidChange = false;
      }
      if(mapRegionDidChange) {
        // after map region change the relative coordinates range between 48 - 55
        mapRegionX = (teleportToX>>3)-6;
        mapRegionY = (teleportToY>>3)-6;
        
        //playerListSize = 0;
        // completely rebuild playerList after teleport AND map region change
      }
      
      currentX = teleportToX - 8*mapRegionX;
      currentY = teleportToY - 8*mapRegionY;
      absX = teleportToX;
      absY = teleportToY;
      resetWalkingQueue();
      
      teleportToX = teleportToY = -1;
      didTeleport = true;
    } else {
      dir1 = getNextWalkingDirection();
      if(dir1 == -1) return;		// standing
      
      if(isRunning) {
        dir2 = getNextWalkingDirection();
      }
      
      // check, if we're required to change the map region
      int deltaX = 0, deltaY = 0;
      if(currentX < 2*8) {
        deltaX = 4*8;
        mapRegionX -= 4;
        mapRegionDidChange = true;
      } else if(currentX >= 11*8) {
        deltaX = -4*8;
        mapRegionX += 4;
        mapRegionDidChange = true;
      }
      if(currentY < 2*8) {
        deltaY = 4*8;
        mapRegionY -= 4;
        mapRegionDidChange = true;
      } else if(currentY >= 11*8) {
        deltaY = -4*8;
        mapRegionY += 4;
        mapRegionDidChange = true;
      }
      
      if(mapRegionDidChange) {
        // have to adjust all relative coordinates
        currentX += deltaX;
        currentY += deltaY;
        for(int i = 0; i < walkingQueueSize; i++) {
          walkingQueueX[i] += deltaX;
          walkingQueueY[i] += deltaY;
        }
      }
      
    }
  }
  
  // handles anything related to character position, i.e. walking,running and teleportation
  // applies only to the char and the client which is playing it
  public void updateThisPlayerMovement(stream str) {
    if(mapRegionDidChange) {
      str.createFrame(73);
      str.writeWordA(mapRegionX+6);	// for some reason the client substracts 6 from those values
      str.writeWord(mapRegionY+6);
    }
    
    if(didTeleport == true) {
      str.createFrameVarSizeWord(81);
      str.initBitAccess();
      str.writeBits(1, 1);
      str.writeBits(2, 3);			// updateType
      str.writeBits(2, heightLevel);
      str.writeBits(1, 1);			// set to true, if discarding (clientside) walking queue
      str.writeBits(1, (updateRequired) ? 1 : 0);
      str.writeBits(7, currentY);
      str.writeBits(7, currentX);
      if (IsDead == true && IsDeadTimer == true && IsDeadTeleporting == true) {
        IsDead = false;
        IsDeadTimer = false;
        SafeMyLife = false;
        IsUsingSkill = false;
      }
      return ;
    }
    
    if(dir1 == -1) {
      // don't have to update the character position, because we're just standing
      str.createFrameVarSizeWord(81);
      str.initBitAccess();
      if(updateRequired) {
        // tell client there's an update block appended at the end
        str.writeBits(1, 1);
        str.writeBits(2, 0);
      } else {
        str.writeBits(1, 0);
      }
      if (DirectionCount < 50) {
        DirectionCount++;
      }
    } else {
      DirectionCount = 0;
      str.createFrameVarSizeWord(81);
      str.initBitAccess();
      str.writeBits(1, 1);
      
      if(dir2 == -1) {
        // send "walking packet"
        str.writeBits(2, 1);		// updateType
        str.writeBits(3, Library.mutil.xlateDirectionToClient[dir1]);
        if(updateRequired) str.writeBits(1, 1);		// tell client there's an update block appended at the end
        else str.writeBits(1, 0);
      } else {
        // send "running packet"
        str.writeBits(2, 2);		// updateType
        str.writeBits(3, Library.mutil.xlateDirectionToClient[dir1]);
        str.writeBits(3, Library.mutil.xlateDirectionToClient[dir2]);
        if(updateRequired) str.writeBits(1, 1);		// tell client there's an update block appended at the end
        else str.writeBits(1, 0);
        if (playerEnergy > 0) {
          playerEnergy -= 1;
        } else {
          isRunning2 = false;
          stoprunning = true;
        }
      }
    }
    
  }
  
  // handles anything related to character position basically walking, running and standing
  // applies to only to "non-thisPlayer" characters
  public void updatePlayerMovement(stream str) {
    if(dir1 == -1) {
      // don't have to update the character position, because the char is just standing
      if(updateRequired || chatTextUpdateRequired) {
        // tell client there's an update block appended at the end
        str.writeBits(1, 1);
        str.writeBits(2, 0);
      } else str.writeBits(1, 0);
    } else if(dir2 == -1) {
      // send "walking packet"
      str.writeBits(1, 1);
      str.writeBits(2, 1);
      str.writeBits(3, Library.mutil.xlateDirectionToClient[dir1]);
      str.writeBits(1, (updateRequired || chatTextUpdateRequired) ? 1: 0);
    } else {
      // send "running packet"
      str.writeBits(1, 1);
      str.writeBits(2, 2);
      str.writeBits(3, Library.mutil.xlateDirectionToClient[dir1]);
      str.writeBits(3, Library.mutil.xlateDirectionToClient[dir2]);
      str.writeBits(1, (updateRequired || chatTextUpdateRequired) ? 1: 0);
    }
  }
  
  
  public boolean dropsitem = false;
  public void removeequipped() {
    dropsitem = true;
  }
  public void setPlrAnimation(int i) {
    pEmote = i;
    updateRequired = true;
    appearanceUpdateRequired = true;
  }
  public int GetPlrBlockAnim(int id) {
    if(id == 4755) // veracs flail
    {
      return 2063;
    }
    if(id == 4153) // maul
    {
      return 1666;
    } else {
      return 1834;
    }
  }
  // a bitmap of players that we want to keep track of whether char appearance has changed so
  // we know if we have to transmit those or can make use of the cached char appearances in the client
  public byte cachedPropertiesBitmap[] = new byte[(ClientUpdater.maxPlayers+7) >> 3];
  
  public void addNewNPC(NPC npc, stream str, stream updateBlock) {
    int id = npc.npcId;
    npcInListBitmap[id >> 3] |= 1 << (id&7);	// set the flag
    npcList[npcListSize++] = npc;
    
    str.writeBits(14, id);	// client doesn't seem to like id=0
    
    int z = npc.absY-absY;
    if(z < 0) z += 32;
    str.writeBits(5, z);	// y coordinate relative to thisPlayer
    z = npc.absX-absX;
    if(z < 0) z += 32;
    str.writeBits(5, z);	// x coordinate relative to thisPlayer
    
    str.writeBits(1, 0); //something??
    str.writeBits(12, npc.npcType);
    
    boolean savedUpdateRequired = npc.updateRequired;
    npc.updateRequired = true;
    npc.appendNPCUpdateBlock(updateBlock);
    npc.updateRequired = savedUpdateRequired;
    str.writeBits(1, 1); // update required
  }
  
  public void addNewPlayer(Player plr, stream str, stream updateBlock) {
    int id = plr.playerId;
    playerInListBitmap[id >> 3] |= 1 << (id&7);	// set the flag
    playerList[playerListSize++] = plr;
    
    str.writeBits(11, id);	// client doesn't seem to like id=0
    
    // TODO: properly implement the character appearance handling
    // send this everytime for now and don't make use of the cached ones in client
    str.writeBits(1, 1);	// set to true, if player definitions follow below
    boolean savedFlag = plr.appearanceUpdateRequired;
    boolean savedUpdateRequired = plr.updateRequired;
    plr.appearanceUpdateRequired = true;
    plr.updateRequired = true;
    plr.appendPlayerUpdateBlock(updateBlock);
    plr.appearanceUpdateRequired = savedFlag;
    plr.updateRequired = savedUpdateRequired;
    
    
    str.writeBits(1, 1);	// set to true, if we want to discard the (clientside) walking queue
    // no idea what this might be useful for yet
    int z = plr.absY-absY;
    if(z < 0) z += 32;
    str.writeBits(5, z);	// y coordinate relative to thisPlayer
    z = plr.absX-absX;
    if(z < 0) z += 32;
    str.writeBits(5, z);	// x coordinate relative to thisPlayer
  }
  
  
  
  // player appearance related stuff
  public boolean appearanceUpdateRequired = true;	// set to true if the player appearance wasn't synchronized
  // with the clients yet or changed recently
  
  public static stream playerProps;
  static {
    playerProps = new stream(new byte[100]);
  }
  public void appendPlayerAppearance(stream str) {
    playerProps.currentOffset = 0;
    
    // TODO: yet some things to figure out on this block + properly implement this
    playerProps.writeByte(playerLook[0]);		// player sex. 0=Male and 1=Female
    //playerProps.writeByte(1 & 1 >> 2);		// playerStatusMask - skull, prayers etc alkup 0
    playerProps.writeByte(headIcon);
    
    // defining the character shape - 12 slots following - 0 denotes a null entry and just a byte is used
    // slot 0,8,11,1 is head part - missing additional things are beard and eyepatch like things either 11 or 1
    // cape, apron, amulet... the remaining things...
    
    if (isNpc == false) {
      if (playerEquipment[playerHat] > 1) {
        playerProps.writeWord(0x200 + playerEquipment[playerHat]);
      } else {
        playerProps.writeByte(0);
      }
      if (playerEquipment[playerCape] > 1) {
        playerProps.writeWord(0x200 + playerEquipment[playerCape]);
      } else {
        playerProps.writeByte(0);
      }
      if (playerEquipment[playerAmulet] > 1) {
        playerProps.writeWord(0x200 + playerEquipment[playerAmulet]);
      } else {
        playerProps.writeByte(0);
      }
      if (playerEquipment[playerWeapon] > 1 && !IsWcing) {
        playerProps.writeWord(0x200 + playerEquipment[playerWeapon]);
      } else if (playerAxe > 0 && IsWcing) {
        playerProps.writeWord(0x200 + playerAxe);
      } else {
        playerProps.writeByte(0);
      }
      if (playerEquipment[playerChest] > 1) {
        playerProps.writeWord(0x200 + playerEquipment[playerChest]);
      } else {
        playerProps.writeWord(0x100+pTorso);
      }
      if (playerEquipment[playerShield] > 1 && !IsWcing) {
        playerProps.writeWord(0x200 + playerEquipment[playerShield]);
      } else {
        playerProps.writeByte(0);
      }
      if (!Item.isPlate(playerEquipment[playerChest])) {
        playerProps.writeWord(0x100+pArms);
      } else {
        playerProps.writeByte(0);
      }
      if (playerEquipment[playerLegs] > 1) {
        playerProps.writeWord(0x200 + playerEquipment[playerLegs]);
      } else {
        playerProps.writeWord(0x100+pLegs);
      }
      if (!Item.isFullHelm(playerEquipment[playerHat]) && !Item.isFullMask(playerEquipment[playerHat])) {
        playerProps.writeWord(0x100 + pHead);		// head
      } else {
        playerProps.writeByte(0);
      }
      if (playerEquipment[playerHands] > 1) {
        playerProps.writeWord(0x200 + playerEquipment[playerHands]);
      } else {
        playerProps.writeWord(0x100+pHands);
      }
      if (playerEquipment[playerFeet] > 1) {
        playerProps.writeWord(0x200 + playerEquipment[playerFeet]);
      } else {
        playerProps.writeWord(0x100+pFeet);
      }
      if (!Item.isFullHelm(playerEquipment[playerHat]) && !Item.isFullMask(playerEquipment[playerHat]) &&(playerLook[0] != 1)) {
        playerProps.writeWord(0x100 + pBeard); // Beard
      } else {
        playerProps.writeByte(0);
      }
      
      //playerProps.writeByte(0);
    } else {
      playerProps.writeWord(-1);
      playerProps.writeWord(npcId);
      
    }
    // array of 5 bytes defining the colors
    playerProps.writeByte(playerLook[1]);	// hair color
    playerProps.writeByte(playerLook[2]);	// torso color.
    playerProps.writeByte(playerLook[3]);	// leg color
    playerProps.writeByte(playerLook[4]);	// feet color
    playerProps.writeByte(playerLook[5]);	// skin color (0-6)
    
    playerProps.writeWord(pEmote);		// standAnimIndex
    playerProps.writeWord(0x337);		// standTurnAnimIndex
    playerProps.writeWord(playerSEW);	// walkAnimIndex
    playerProps.writeWord(0x334);		// turn180AnimIndex
    playerProps.writeWord(0x335);		// turn90CWAnimIndex
    playerProps.writeWord(0x336);		// turn90CCWAnimIndex
    playerProps.writeWord(playerSER);	// runAnimIndex
    
    playerProps.writeQWord(Library.mutil.playerNameToInt64(playerName));
    
    //Stat fix, combat decreases when your hp or any of these skills get lowerd, this fixes that problem.
    
    int mag = (int)((double)(getLevelForXP(playerXP[4])) * 1.5);
    int ran = (int)((double)(getLevelForXP(playerXP[6])) * 1.5);
    int attstr = (int)((double)(getLevelForXP(playerXP[0])) + (double)(getLevelForXP(playerXP[2])));
    
    int combatLevel = 0;
    if (ran > attstr) {
      combatLevel = (int)(((double)(getLevelForXP(playerXP[1])) * 0.25) + ((double)(getLevelForXP(playerXP[3])) * 0.25) + ((double)(getLevelForXP(playerXP[5])) * 0.125) + ((double)(getLevelForXP(playerXP[6])) * 0.4875));
    } else if (mag > attstr) {
      combatLevel = (int)(((double)(getLevelForXP(playerXP[1])) * 0.25) + ((double)(getLevelForXP(playerXP[3])) * 0.25) + ((double)(getLevelForXP(playerXP[5])) * 0.125) + ((double)(getLevelForXP(playerXP[4])) * 0.4875));
    } else {
      combatLevel = (int)(((double)(getLevelForXP(playerXP[1])) * 0.25) + ((double)(getLevelForXP(playerXP[3])) * 0.25) + ((double)(getLevelForXP(playerXP[5])) * 0.125) + ((double)(getLevelForXP(playerXP[0])) * 0.325) + ((double)(getLevelForXP(playerXP[2])) * 0.325));
    }
    
    combat = combatLevel;
    if(playerRights <= 1 && playerRights >= 0) playerProps.writeByte(combatLevel);
	else if(playerRights <= 3 && playerRights >= 2) playerProps.writeByte(CombatMask);

    
    playerProps.writeWord(0);			// incase != 0, writes skill-%d
    str.writeByteC(playerProps.currentOffset);		// size of player appearance block
    str.writeBytes(playerProps.buffer, playerProps.currentOffset, 0);
  }
  
  public boolean chatTextUpdateRequired = false;
  public byte chatText[] = new byte[4096], chatTextSize = 0;
  public int chatTextEffects = 0, chatTextColor = 0;
  public void appendPlayerChatText(stream str) {
    str.writeWordBigEndian(((chatTextColor&0xFF) << 8) + (chatTextEffects&0xFF));
    str.writeByte(playerRights);
    str.writeByteC(chatTextSize);		// no more than 256 bytes!!!
    str.writeBytes_reverse(chatText, chatTextSize, 0);
  }
  public boolean update1Required = false;
  public int update1_1 = 0;
  public int update1_2 = 0;
  public void appendUpdate1(stream str) {
    str.writeWordBigEndian(update1_1);
    str.writeByteC(update1_2);
  }
  public void appendPlayerUpdateBlock(stream str) {
    if(!updateRequired && !chatTextUpdateRequired) return;		// nothing required
    int updateMask = 0;
    if(mask400update) updateMask |= 0x400;
    if(mask100update) updateMask |= 0x100;
    if(animationRequest != -1) updateMask |= 8;
    if(string4UpdateRequired) updateMask |= 4;
    if(appendMask4) updateMask |= 4;
    if(chatTextUpdateRequired) updateMask |= 0x80;
    if(faceNPCupdate) updateMask |= 1;
    if(appearanceUpdateRequired) updateMask |= 0x10;
    if(FocusPointX != -1) updateMask |= 2;
    if(hitUpdateRequired) updateMask |= 0x200;
    if(dirUpdateRequired) updateMask |= 0x40;
    if(dirUpdate2Required) updateMask |= 2;
    if(animationRequest != -1) updateMask |= 8;
    
    if(updateMask >= 0x100) {
      // byte isn't sufficient
      updateMask |= 0x40;			// indication for the client that updateMask is stored in a word
      str.writeByte(updateMask & 0xFF);
      str.writeByte(updateMask >> 8);
    } else str.writeByte(updateMask);
    
    // now writing the various update blocks itself - note that their order crucial
    if(mask400update)   appendMask400Update(str);
    if(mask100update)   appendMask100Update(str);
    if(animationRequest != -1) appendAnimationRequest(str);
    if(string4UpdateRequired) appendString4(str);
    if(appendMask4) appendMask4(str);
    if(chatTextUpdateRequired) appendPlayerChatText(str);
    if(faceNPCupdate)   appendFaceNPCUpdate(str);
    if(appearanceUpdateRequired) appendPlayerAppearance(str);
    if(FocusPointX != -1) appendSetFocusDestination(str);
    if(hitUpdateRequired) appendHitUpdate(str);
    if(dirUpdateRequired) appendDirUpdate(str);
    if(dirUpdate2Required) appendDirUpdate2(str);
  }
  public String Mask4String = "";
  public boolean appendMask4 = false;
  public void setMask4(String textstring){
    Mask4String = textstring;
    updateRequired = true;
    appearanceUpdateRequired = true;
    appendMask4 = true;
  }
  
  public void appendMask4(stream str) {
    str.writeString(Mask4String);
  }
  public void clearUpdateFlags() {
    updateRequired = false;
    //animationRequest = -1;
    string4UpdateRequired = false;
    chatTextUpdateRequired = false;
    appendMask4 = false;
    appearanceUpdateRequired = false;
    FocusPointX = FocusPointY = -1;
    hitUpdateRequired = false;
    dirUpdateRequired = false;
    animationRequest = -1;
    dirUpdate2Required = false;
    faceNPCupdate = false;
    faceNPC = 65535;
    mask100update = false;
    update1Required = false;
    IsStair = false;
  }
  
  
  
  public static int newWalkCmdX[] = new int[walkingQueueSize];
  public static int newWalkCmdY[] = new int[walkingQueueSize];
  public static int tmpNWCX[] = new int[walkingQueueSize];
  public static int tmpNWCY[] = new int[walkingQueueSize];
  public static int newWalkCmdSteps = 0;
  public static boolean newWalkCmdIsRunning = false;
  public static int travelBackX[] = new int[walkingQueueSize];
  public static int travelBackY[] = new int[walkingQueueSize];
  public static int numTravelBackSteps = 0;
  
  public void preProcessing() {
    newWalkCmdSteps = 0;
  }
  
  // is being called regularily every 500ms - do any automatic player actions herein
  public abstract boolean process();
  public boolean following = false;
  public void postProcessing() {
    if(newWalkCmdSteps > 0) {
      int OldcurrentX = currentX;
      int OldcurrentY = currentY;
      for(i = 0; i < playerFollow.length; i++) {
        if (playerFollow[i] != -1 && following == true) {
          ClientUpdater.players[playerFollow[i]].newWalkCmdSteps = (newWalkCmdSteps + 1);
          for(int j = 0; j < newWalkCmdSteps; j++) {
            ClientUpdater.players[playerFollow[i]].newWalkCmdX[(j + 1)] = newWalkCmdX[j];
            ClientUpdater.players[playerFollow[i]].newWalkCmdY[(j + 1)] = newWalkCmdY[j];
          }
          ClientUpdater.players[playerFollow[i]].newWalkCmdX[0] = OldcurrentX;
          ClientUpdater.players[playerFollow[i]].newWalkCmdY[0] = OldcurrentY;
          ClientUpdater.players[playerFollow[i]].poimiX = OldcurrentX;
          ClientUpdater.players[playerFollow[i]].poimiY = OldcurrentY;
        }
        
        
        // place this into walking queue
        // care must be taken and we can't just append this because usually the starting point (clientside) of
        // this packet and the current position (serverside) do not coincide. Therefore we might have to go
        // back in time in order to find a proper connecting vertex. This is also the origin of the character
        // walking back and forth when there's noticeable lag and we keep on seeding walk commands.
        int firstX = newWalkCmdX[0], firstY = newWalkCmdY[0];	// the point we need to connect to from our current position...
        
        // travel backwards to find a proper connection vertex
        int lastDir = 0;
        boolean found = false;
        numTravelBackSteps = 0;
        int ptr = wQueueReadPtr;
        int dir = Library.mutil.direction(currentX, currentY, firstX, firstY);
        if(dir != -1 && (dir&1) != 0) {
          // we can't connect first and current directly
          do {
            lastDir = dir;
            if(--ptr < 0) ptr = walkingQueueSize-1;
            
            travelBackX[numTravelBackSteps] = walkingQueueX[ptr];
            travelBackY[numTravelBackSteps++] = walkingQueueY[ptr];
            dir = Library.mutil.direction(walkingQueueX[ptr], walkingQueueY[ptr], firstX, firstY);
            if(lastDir != dir) {
              found = true;
              break;		// either of those two, or a vertex between those is a candidate
            }
            
          } while(ptr != wQueueWritePtr);
        } else found = true;	// we didn't need to go back in time because the current position
        // already can be connected to first
        
        if(!found) {
          disconnected = true;
        } else {
          wQueueWritePtr = wQueueReadPtr;		// discard any yet unprocessed waypoints from queue
          
          addToWalkingQueue(currentX, currentY);	// have to add this in order to keep consistency in the queue
          
          if(dir != -1 && (dir&1) != 0) {
            // need to place an additional waypoint which lies between walkingQueue[numTravelBackSteps-2] and
            // walkingQueue[numTravelBackSteps-1] but can be connected to firstX/firstY
            
            for(int i = 0; i < numTravelBackSteps-1; i++) {
              addToWalkingQueue(travelBackX[i], travelBackY[i]);
            }
            int wayPointX2 = travelBackX[numTravelBackSteps-1], wayPointY2 = travelBackY[numTravelBackSteps-1];
            int wayPointX1, wayPointY1;
            if(numTravelBackSteps == 1) {
              wayPointX1 = currentX;
              wayPointY1 = currentY;
            } else {
              wayPointX1 = travelBackX[numTravelBackSteps-2];
              wayPointY1 = travelBackY[numTravelBackSteps-2];
            }
            // we're coming from wayPoint1, want to go in direction wayPoint2 but only so far that
            // we get a connection to first
            
            // the easiest, but somewhat ugly way:
            // maybe there is a better way, but it involves shitload of different
            // cases so it seems like it isn't anymore
            dir = Library.mutil.direction(wayPointX1, wayPointY1, wayPointX2, wayPointY2);
            if(dir == -1 || (dir&1) != 0) {
              //println_debug("Fatal: The walking queue is corrupt! wp1=("+wayPointX1+", "+wayPointY1+"), "+
              //    "wp2=("+wayPointX2+", "+wayPointY2+")");
            } else {
              dir >>= 1;
              found = false;
              int x = wayPointX1, y = wayPointY1;
              while(x != wayPointX2 || y != wayPointY2) {
                x += Library.mutil.directionDeltaX[dir];
                y += Library.mutil.directionDeltaY[dir];
                if((Library.mutil.direction(x, y, firstX, firstY)&1) == 0) {
                  found = true;
                  break;
                }
              }
			if(found) addToWalkingQueue(wayPointX1, wayPointY1); //recently changed
            }
          } else {
            for(int i = 0; i < numTravelBackSteps; i++) {
              addToWalkingQueue(travelBackX[i], travelBackY[i]);
            }
          }
          
          // now we can finally add those waypoints because we made sure about the connection to first
          for(int i = 0; i < newWalkCmdSteps; i++) {
            addToWalkingQueue(newWalkCmdX[i], newWalkCmdY[i]);
          }
          
        }
        isRunning = newWalkCmdIsRunning || isRunning2;
        
        for(i = 0; i < playerFollow.length; i++) {
          if (playerFollow[i] != -1 && ClientUpdater.players[playerFollow[i]] != null) {
            ClientUpdater.players[playerFollow[i]].postProcessing();
          }
        }
      }
    }
  }
  
  public void kick() {
    isKicked = true;
  }
  
  public int hitDiff = 0;
  public boolean hitUpdateRequired = false;
  public boolean IsDead = false;
  public int NewHP = 10;
  public boolean SafeMyLife = false;
  public boolean IsStair = false;
  public boolean IsDeadTeleporting = false;
  public boolean IsDeadTimer = false;
  public void appendHitUpdate2(stream str) {
    try {
      str.writeByte(hitDiff); // What the perseon got 'hit' for
      if (hitDiff > 0 && newhptype == false && poisondmg == false) {
        str.writeByteA(1); // 0: red hitting - 1: blue hitting
      } else if (hitDiff > 0 && poisondmg == true) {
        str.writeByteA(2); // 0: red hitting - 1: blue hitting 2: poison 3: disease
      } else if (hitDiff > 0 && newhptype == true) {
        str.writeByteA(hptype); // 0: red hitting - 1: blue hitting
      } else {
        str.writeByteA(0); // 0: red hitting - 1: blue hitting
      }
      NewHP = (playerLevel[playerHitpoints] - hitDiff);
      if (NewHP <= 0) {
        NewHP = 0;
        IsDead = true;
      }
      str.writeByteC(NewHP); // Their current hp, for HP bar
      str.writeByte(getLevelForXP(playerXP[playerHitpoints])); // Their max hp, for HP bar
      poisondmg = false;
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
  public void appendHitUpdate(stream str) {
    try {
      str.writeByte(hitDiff); // What the perseon got 'hit' for
      if (hitDiff > 0 && newhptype == false && poisondmg == false) {
        str.writeByteS(1); // 0: red hitting - 1: blue hitting
      } else if (hitDiff > 0 && poisondmg == true) {
        str.writeByteS(2); // 0: red hitting - 1: blue hitting 2: poison 3: disease
      } else if (hitDiff > 0 && newhptype == true) {
        str.writeByteS(hptype); // 0: red hitting - 1: blue hitting
      } else {
        str.writeByteS(0); // 0: red hitting - 1: blue hitting
      }
      NewHP = (playerLevel[playerHitpoints] - hitDiff);
      if (NewHP <= 0) {
        NewHP = 0;
        IsDead = true;
      }
      str.writeByte(NewHP); // Their current hp, for HP bar
      str.writeByteC(getLevelForXP(playerXP[playerHitpoints])); // Their max hp, for HP bar
      poisondmg = false;
    } catch(Exception e) {
      e.printStackTrace();
    }
  }
  public int getLevelForXP(double exp) {
    int points = 0;
    int output = 0;
    
    for (int lvl = 1; lvl <= 150; lvl++) {
      points += Math.floor((double)lvl + 300.0 * Math.pow(2.0, (double)lvl / 7.0));
      output = (int)Math.floor(points / 4);
      if (output >= exp)
        return lvl;
    }
    return 0;
  }
  public int animationRequest = -1, animationWaitCycles = 0;
  public boolean animationUpdateRequired = false;
  public void Animation(int animIdx) {
    animationRequest = animIdx;
    animationWaitCycles = 0;
	appearanceUpdateRequired = true;
	animationUpdateRequired = true;
  }
  public void appendAnimationRequest(stream str) {
    str.writeWordBigEndian((animationRequest==-1) ? 65535 : animationRequest);
    str.writeByteC(animationWaitCycles);
  }
  public int playerEmotionReq = -1;
  public int playerEmotionDelay = 0;
  public void appendEmotionUpdate(stream str) {
    str.writeWordBigEndian((playerEmotionReq==-1) ? 65535 : playerEmotionReq);
    str.writeByteC(playerEmotionDelay);
  }
  public int mask1var = 0;
  public boolean mask1update = false;
  public void appendMask1Update(stream str) {
    str.writeWordBigEndian(mask1var);
  }
  public void faceNPC(int index) {
    faceNPC = index;
    faceNPCupdate = true;
    updateRequired = true;
  }
  public boolean faceNPCupdate = false;
  public int faceNPC = -1;
  public void appendFaceNPCUpdate(stream str) {
    str.writeWordBigEndian(faceNPC);
  }
  public int mask100var1 = 0;
  public int mask100var2 = 0;
  public boolean mask100update = false;
  public void appendMask100Update(stream str) {
    str.writeWordBigEndian(mask100var1);
    str.writeDWord(mask100var2);
  }
  public int m4001 = 0;
  public int m4002 = 0;
  public int m4003 = 0;
  public int m4004 = 0;
  public int m4005 = 0;
  public int m4006 = 0;
  public int m4007 = 0;
  public boolean mask400update = false;
  public void appendMask400Update(stream str) {
    str.writeByteA(m4001);
    str.writeByteA(m4002);
    str.writeByteA(m4003);
    str.writeByteA(m4004);
    str.writeWordA(m4005);
    str.writeWordBigEndianA(m4006);
    str.writeByteA(m4007); // direction
  }
  public String txt4 = "";
  public boolean string4UpdateRequired = false;
  public void appendString4(stream str) {
    str.writeString(txt4);
  }
  public void appendDirUpdate2(stream str) {
    str.writeWordBigEndianA(viewToX);
    str.writeWordBigEndian(viewToY);
  }
  public void appendDirUpdate(stream str) {
    if (playerMD != -1) {
      str.writeWord(playerMD);
      playerMD = -1;
    }
  }
  public boolean[] IsDropped = new boolean[Server.itemHandler.MaxDropItems];
  public boolean[] MustDelete = new boolean[Server.itemHandler.MaxDropItems];
  public boolean IsDropping = false;
  
  //PM Stuff
  public abstract boolean isinpm(long l);
  public abstract void loadpm(long l, int world);
  public abstract void pmupdate(int pmid, int world);
  public int Privatechat = 0;
  public abstract void sendpm(long name,int rights,byte[] chatmessage, int messagesize);
  public long friends[] = new long[50];
  public long ignores[] = new long[50];
  public boolean IsPMLoaded = false;
  public int playerMessages;
  public String playerLastConnect;
  public int playerLastLogin;
  public int playerEnergy;
  public int playerEnergyGian;
  public int playerLook[] = new int[6];
  public boolean IsUsingSkill = false;
  public int playerBonus[] = new int[12];
  public int StrPotion = 0;
  public int StrPrayer = 0;
  public int FightType = 1;
  public int playerMaxHit = 0;
  public int playerSE = 0x328; //SE = Standard Emotion
  public int playerSEW = 0x333; //SEW = Standard Emotion Walking
  public int playerSER = 0x338; //SER = Standard Emotion Run
  public int playerSEA = 0x326; //SEA = Standard Emotion Attack
  public int playerMD = -1;
  public int viewToX = -1;
  public int viewToY = -1;
  public boolean dirUpdateRequired = false;
  public boolean dirUpdate2Required = false;
  public boolean IsCutting = false;
  public boolean WannePickUp = false;
  public boolean IsInWilderness = false;
  public boolean IsAttacking = false;
  public boolean IsMining = false;
  public boolean IsAttackingNPC = false;
  public int attacknpc = -1;
  public int Essence;
  public boolean IsShopping = false;
  public int MyShopID = 0;
  public boolean UpdateShop = false;
  public boolean RebuildNPCList = false;
  public int IsSnowing = 0;
  public int NpcDialogue = 0;
  public int NpcTalkTo = 0;
  public boolean NpcDialogueSend = false;
  public int NpcWanneTalk = 0;
  public boolean IsBanking = false;
  public int WanneTrade = 0;
  public int WanneTradeWith = 0;
  public boolean TradeConfirmed = false;
  public boolean AntiTradeScam = false;
  public int playerFollow[] = new int[ClientUpdater.maxPlayers];
  public int playerFollowID = -1;
  public int FollowingID = -1;
  public int DirectionCount = 0;
  public boolean playerAncientMagics = false;
  public String playerServer;
  public int playerGameTime;
  public int playerGameCount;
public String myClanName = "none";
public int clanRights = 0;
public boolean playerInvisable = false;
public boolean autoCasting = false;
public boolean ancientStaff = false;
public int autoCastSpell = -1;
public int autoCastMenu = -1;
public int workout = 518400;
public int afk = 0;
public int startcw = 0;
public int playerIsMember = 0;
public int PlayerKilling = 0;
public int cannontimer = 0;
public int Saradominban = 0;
public int zammorakban = 0;
public int playerissara = 0;
public int playeriszammy = 0;
public boolean showingCity = false;
public int showCityTimer = 0;
public int cityX = 0;
public int cityY = 0;
public boolean drunk = false;
public int drunkTimer = 0;
public boolean newAnimRequired = false;
public int newAnimDelay = 0;
public int newAnim = 0;
public int mageTimer = 0;
public int DarkClan = 0;
public int HeroClan = 0;
public int spec = 5; // specs 
public int specTimer = 0; // specs
/*Shout Box*/
public int SMuted = 0;
public int SRights = 0;
//Skills
  public int Amining[] = { 0, 0, 0, };
  public int Awoodcutting[] = { 0, 0, 0, };
  public int Asmithing[] = { 0, 0, 0, 0, 0, };
  public int Asmelting[] = { 0, 0, 0, 0, };
  public int Afishing[] = { 0, 0, 0, 0, };
  // Afishing[0] = Fish ID
  // Afishing[1] = Fish XP
  // Afishing[2] = Animation
  // Afishing[3] = Timer
  public String[] quest = new String[500];
//world
  public int worldtype = 1; // 0 = f2p, 1 = mem
  public boolean member = true; // member or not
  public int num = 1;
//xp
  public int byte28 = 9; // range 6
  public int byte29 = 9; // attack6
  public int byte30 = 9; // strength6
  public int byte31 = 9; // defence6
  public int byte40 = 6; // hits3
//unlocked emotes
  public int EmoGlassBox = 0; //MIME
  public int EmoClimbRope = 0; //MIME
  public int EmoLean = 0; //MIME
  public int EmoGlassWall = 0; //MIME
  public int EmoGoblinBow = 0; //QUEST: Goblin Diplomacy
  public int EmoGoblinDance = 0; //QUEST: Goblin Diplomacy
  public int EmoScared = 0; //HALLOWEEN
//Fighting timers
  public int LoopHitDelay = 0;
//Macro Detection
  public boolean hasFocus;
//Chat settings
  public int configPublicChat = 0; // 0 on, 1 friends, 2 off, 3 hide.
  public int configTradeChat = 0; // 0 on, 1 friends, 2 off
//ReenScript
}